
#pragma once

#include <vector>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>


namespace Stitches
{
using Vec2 = glm::i32vec2;
using Vec2f = glm::vec2;
using Vec2d = glm::dvec2;
using Vec3f = glm::vec3;
using Vec3d = glm::dvec3;
using Color3f = glm::vec3;
using Color4f = glm::vec4;
using Colour = glm::vec4;

using Vec4 = glm::i32vec4;
using Vec4d = glm::dvec3;
using Vec4f = glm::vec4;
using Vec4i = glm::i32vec4;
using Vec3i = glm::i32vec4;


using Vec2i = Vec2;
using Vec2ui = glm::u32vec2;
using Bool2 = glm::bvec2;
using Bool3 = glm::bvec3;
using Bool4 = glm::bvec4;

using Int2 = glm::i32vec2;
using Int3 = glm::i32vec3;
using Int4 = glm::i32vec4;

using Uint2 = glm::u32vec2;
using Uint3 = glm::u32vec3;
using Uint4 = glm::u32vec4;

using Mat4 = glm::mat4x4;
using Mat3 = glm::dmat3x3;

using LinearColor = Vec3f;

//常量
const double infinity = std::numeric_limits<double>::infinity(); //无穷大
const double pi = 3.1415926535897932385;

//函数
inline double degrees_to_radians(double degrees)
{
    //度数到弧度
    return degrees * pi / 180.0;
}

inline double random_double()
{
    //[0,1)
    return rand() / (RAND_MAX + 1.0);
}

inline double random_double(double min, double max)
{
    //[min,max)
    return min + (max - min) * random_double();
}

inline Vec3f random_in_unit_disk()
{
    while (true) {
        auto p = Vec3f(random_double(-1, 1), random_double(-1, 1), 0);
        if (glm::dot(p, p) >= 1)
            continue;
        return p;
    }
}

inline double clamp(double x, double min, double max)
{
    if (x < min) return min;
    if (x > max) return max;
    return x;
}

inline Vec3f randomVec3()
{
    return Vec3f(random_double(), random_double(), random_double());
}

inline Vec3f randomVec3(double min, double max)
{
    return Vec3f(random_double(min, max), random_double(min, max), random_double(min, max));
}

inline Vec3f randomInUnitSphere()
{
    while (true)
    {
        Vec3f p = randomVec3(-1, 1);
        if (glm::dot(p, p) >= 1)
        {
            continue;
        }
        return p;
    }
}

inline Vec3f  randomInHemisphere(const Vec3f& normal)
{
    Vec3f in_unit_sphere = randomInUnitSphere();
    if (dot(in_unit_sphere, normal) > 0.0)// In the same hemisphere as the normal
        return in_unit_sphere;
    else
        return -in_unit_sphere;
}

inline Vec3f randomUnitVector()
{
    auto a = random_double(0, 2 * pi);
    auto z = random_double(-1, 1);
    auto r = sqrt(1 - z * z);
    return Vec3f(r * cos(a), r * sin(a), z);
}

inline Vec3f reflect(const Vec3f& v, const Vec3f& n)
{
    return v - 2 * dot(v, n) * n;
}

inline Vec3f refract(const Vec3f& v, const Vec3f& n, double etai_over_etat)
{
    auto cos_theta = dot(-v, n);
    Vec3f r_out_perp = (float)etai_over_etat * (v + cos_theta * n);
    Vec3f r_out_parallel = (float )-sqrt(fabs(1.0f - glm::dot(r_out_perp, r_out_perp))) * n;
    return r_out_perp + r_out_parallel;
}

inline double schlick(double cosine, double ref_idx)
{
    auto r0 = (1 - ref_idx) / (1 + ref_idx);
    r0 *= r0;
    return r0 + (1 - r0) * pow((1 - cosine), 5);
}


constexpr const double F_E = 2.71828182845904523536028747135266250;
constexpr const double F_LOG2E = 1.44269504088896340735992468100189214;
constexpr const double F_LOG10E = 0.434294481903251827651128918916605082;
constexpr const double F_LN2 = 0.693147180559945309417232121458176568;
constexpr const double F_LN10 = 2.30258509299404568401799145468436421;
constexpr const double F_PI = 3.14159265358979323846264338327950288;
constexpr const double F_PI_2 = 1.57079632679489661923132169163975144;
constexpr const double F_PI_4 = 0.785398163397448309615660845819875721;
constexpr const double F_1_PI = 0.318309886183790671537767526745028724;
constexpr const double F_2_PI = 0.636619772367581343075535053490057448;
constexpr const double F_2_SQRTPI = 1.12837916709551257389615890312154517;
constexpr const double F_SQRT2 = 1.41421356237309504880168872420969808;
constexpr const double F_SQRT1_2 = 0.707106781186547524400844362104849039;
constexpr const double F_TAU = 2.0 * F_PI;

namespace d
{
constexpr const double E = F_E;
constexpr const double LOG2E = F_LOG2E;
constexpr const double LOG10E = F_LOG10E;
constexpr const double LN2 = F_LN2;
constexpr const double LN10 = F_LN10;
constexpr const double PI = F_PI;
constexpr const double PI_2 = F_PI_2;
constexpr const double PI_4 = F_PI_4;
constexpr const double ONE_OVER_PI = F_1_PI;
constexpr const double TWO_OVER_PI = F_2_PI;
constexpr const double TWO_OVER_SQRTPI = F_2_SQRTPI;
constexpr const double SQRT2 = F_SQRT2;
constexpr const double SQRT1_2 = F_SQRT1_2;
constexpr const double TAU = F_TAU;
constexpr const double DEG_TO_RAD = F_PI / 180.0;
constexpr const double RAD_TO_DEG = 180.0 / F_PI;
} // namespace d

namespace f
{
constexpr const float E = (float) d::E;
constexpr const float LOG2E = (float) d::LOG2E;
constexpr const float LOG10E = (float) d::LOG10E;
constexpr const float LN2 = (float) d::LN2;
constexpr const float LN10 = (float) d::LN10;
constexpr const float PI = (float) d::PI;
constexpr const float PI_2 = (float) d::PI_2;
constexpr const float PI_4 = (float) d::PI_4;
constexpr const float ONE_OVER_PI = (float) d::ONE_OVER_PI;
constexpr const float TWO_OVER_PI = (float) d::TWO_OVER_PI;
constexpr const float TWO_OVER_SQRTPI = (float) d::TWO_OVER_SQRTPI;
constexpr const float SQRT2 = (float) d::SQRT2;
constexpr const float SQRT1_2 = (float) d::SQRT1_2;
constexpr const float TAU = (float) d::TAU;
constexpr const float DEG_TO_RAD = (float) d::DEG_TO_RAD;
constexpr const float RAD_TO_DEG = (float) d::RAD_TO_DEG;
} // namespace f
namespace maths
{
template<typename T>
inline constexpr T min(T a, T b) noexcept
{
    return a < b ? a : b;
}

template<typename T>
inline constexpr T max(T a, T b) noexcept
{
    return a > b ? a : b;
}

template<typename T>
inline constexpr T clamp(T v, T v_min, T v_max) noexcept
{
    assert(v_min <= v_max);
    return T(min(v_max, max(v_min, v)));
}

template<typename T>
inline constexpr T saturate(T v) noexcept
{
    return clamp(v, T(0), T(1));
}

template<typename T>
inline constexpr T mix(T x, T y, T a) noexcept
{
    return x * (T(1) - a) + y * a;
}

template<typename T>
inline constexpr T lerp(T x, T y, T a) noexcept
{
    return mix(x, y, a);
}

template<typename T>
inline constexpr T smoothstep(T e0, T e1, T x) noexcept
{
    T t = clamp((x - e0) / (e1 - e0), T(0), T(1));
    return t * t * (T(3) - T(2) * t);
}

template<typename T>
inline constexpr T sign(T x) noexcept
{
    return x < T(0) ? T(-1) : T(1);
}

inline Vec2d bezier(const std::vector<Vec2d> &points, double value) noexcept
{
    if (points.empty())
    {
        return Vec2d();
    }
    else if (points.size() == 1)
    {
        return points[0];
    }
    else if (points.size() == 2)
    {
        //一阶
        return maths::mix(points[0], points[1], Vec2d(value));
    }
    else if (points.size() == 3)
    {
        //二阶
        return maths::mix(maths::mix(points[0], points[1], Vec2d(value)),
                          maths::mix(points[1], points[2], Vec2d(value)),
                          Vec2d(value));
    }
    else if (points.size() == 4)
    {
        //三阶
        Vec2d p0 = maths::mix(points[0], points[1], Vec2d(value));
        Vec2d p1 = maths::mix(points[1], points[2], Vec2d(value));
        Vec2d p2 = maths::mix(points[2], points[3], Vec2d(value));

        return maths::mix(maths::mix(p0, p1, Vec2d(value)),
                          maths::mix(p1, p2, Vec2d(value)),
                          Vec2d(value));
    }
    else
    {
        //四阶以及往上
        std::vector<Vec2d> middlePoints(points.size() - 1);
        for (int i = 0; i < points.size() - 1; i++)
        {
            middlePoints[i] = maths::mix(points[i], points[i + 1], Vec2d(value));
        }
        return bezier(middlePoints, value);
    }
}

inline int random(int start, int end)
{
    int dis = end - start;
    return rand() % dis + start;
}
}

namespace InnerColor
{
static const Colour Black = {0, 0, 0, 1.0};
static const Colour White = {1, 1, 1, 1.0};
static const Colour BackGround = {0.1, 0.125, 0.25, 1.0};
}

}